
/* 
 * File:   main.cpp
 * Author: Davit Vardanyan
 *
 * Created on December 16, 2012, 6:19 PM
 */

#include <cstdlib>
#include <GL/gl.h>
#include <GL/glut.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <cassert>
#include <string>
#include <sstream>
#include <iostream>
#include <time.h>
#include <stack>

#include "BGStone.h"
#include "BGPoint.h"
#include "BGTexture.h"
#include "Constants.c"
#include "ClientSocket.h"
#include "SocketException.h"

using namespace std;

#define STONES_COUNT 30

void init();
void draw();
void display();
void enable (void);
int windowWidth = 915;
int windowHeight = 769;
float center_x = windowWidth / 2;
float center_y = windowHeight / 2;
float center_z = 0.0;
float camera_x = windowWidth / 2;
float camera_y = windowHeight / 2 - 1;
float camera_z = 1000.0;
float light_position[] = { 10.0, 100.0, 0.0, 0.0};
BGStone* myStones;
BGTexture textures(3);
int currentStoneIndex = -1;
int currentStackIndex = -1;
int cube1 = 6;
int cube2 = 2;
BGPoint *stacksPositions;
BGPoint currentStone;
stack<int> *stacks;
int playerId;
int activePlayer = 0;
int port = 30000;
int pointsProgress;

void getCubes()
{
//    string data;
//    ClientSocket clientSocket( "localhost", port );
//    try
//    {
//            ostringstream ss;
//            ss << playerId;
//            string id = ss.str();
//            clientSocket << "ActivePlayer"<<id;
//    }
//    catch ( SocketException& ) {
//    }
}

void enable (void) {
        glEnable (GL_DEPTH_TEST);
//        glEnable (GL_LIGHTING);
        glShadeModel (GL_SMOOTH);
        glEnable (GL_LIGHT1);
        glEnable(GL_NORMALIZE);
        glEnable(GL_COLOR_MATERIAL);
        glEnable(GL_TEXTURE_2D);
//        glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA);
//        glEnable(GL_BLEND);
}

void draw()
{
        glBindTexture(GL_TEXTURE_2D, textures.getTexture(TABLE));
	glLightfv(GL_LIGHT1, GL_POSITION, light_position);
        glBegin(GL_QUADS);
        glColor3f(1.0, 1.0, 1.0);
        glTexCoord2f(1.0, 0.0);
        glVertex3f(0.0, 0.0, 0.0);
        glTexCoord2f(1.0, 1.0);
        glVertex3f(0.0, windowHeight, 0.0);
        glTexCoord2f(0.0, 1.0);
        glVertex3f(windowWidth, windowHeight, 0.0);
        glTexCoord2f(0.0, 0.0);
        glVertex3f(windowWidth, 0.0, 0.0);
        glEnd();
//      this part will remove --------------------------------
//        BGPoint stackPos;
//        for (int i = 12; i >= 0; --i) {
//                stackPos.y = FIRST_STACK_POS_Y - STACK_HEIGHT * ( i / 13 ) ;
//                stackPos.x = FIRST_STACK_POS_X + STACK_WIDTH * ( i % 13 );
//                if (i == 6) {
//                    stackPos.x = FIRST_STACK_POS_X + STACK_WIDTH * ( i % 13 ) + STACK_WIDTH;
//                }
//                glBegin(GL_LINE_LOOP);
//                glColor3f(1.0, 0.0, 0.0);
//                glVertex3f(stackPos.x, stackPos.y, 1.0);
//                glColor3f(1.0, 0.0, 0.0);
//                glVertex3f(stackPos.x, stackPos.y - STACK_HEIGHT, 1.0);
//                glColor3f(1.0, 0.0, 0.0);
//                glVertex3f(stackPos.x + STACK_WIDTH, stackPos.y - STACK_HEIGHT, 1.0);
//                glColor3f(1.0, 0.0, 0.0);
//                glVertex3f(stackPos.x + STACK_WIDTH, stackPos.y, 1.0);
//                glEnd();
//        }
//        for (int i = 13; i < 26; ++i) {
//                stackPos.y = FIRST_STACK_POS_Y - STACK_HEIGHT * ( i / 13 ) ;
//                stackPos.x = FIRST_STACK_POS_X + STACK_WIDTH * ( i % 13 );
//                if (i == 19) {
//                    stackPos.x = FIRST_STACK_POS_X + STACK_WIDTH * ( i % 13 ) + STACK_WIDTH;
//                }
//                glBegin(GL_LINE_LOOP);
//                glColor3f(1.0, 0.0, 0.0);
//                glVertex3f(stackPos.x, stackPos.y, 1.0);
//                glColor3f(1.0, 0.0, 0.0);
//                glVertex3f(stackPos.x, stackPos.y - STACK_HEIGHT, 1.0);
//                glColor3f(1.0, 0.0, 0.0);
//                glVertex3f(stackPos.x + STACK_WIDTH, stackPos.y - STACK_HEIGHT, 1.0);
//                glColor3f(1.0, 0.0, 0.0);
//                glVertex3f(stackPos.x + STACK_WIDTH, stackPos.y, 1.0);
//                glEnd();
//        }

//   ---------------------------------------------------------     
        for (int i = 0; i < STONES_COUNT / 2; i++) {
                myStones[i].draw(textures.getTexture(STONE_BLACK));
        }
                for (int i = STONES_COUNT / 2; i < STONES_COUNT; i++) {
                myStones[i].draw(textures.getTexture(STONE_WHITE));
        }
}

void display()
{
	glClearColor (0.0,0.0,0.0,0.0);
        glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
        enable();
        draw();
        glutSwapBuffers();
        string data;
//        ClientSocket clientSocket( "localhost", port );
//        try
//	{
//                clientSocket << "Stone:" <<currentStoneIndex<<"="<<myStones[currentStoneIndex].getPosition();
//                clientSocket >> data;
//                cout<<data<<endl;
//	}
//	catch ( SocketException& ) {}

}

void push(int stone, int stack)
{
    int size = stacks[stack].size();
    BGPoint pos;
    pos.x = stacksPositions[stack].x + STACK_WIDTH / 2;
    if (size < 6) {
        if (stack < 13) {
            pos.y = stacksPositions[stack].y - STACK_WIDTH / 2 - size * STACK_WIDTH;         
        } else {
            pos.y = stacksPositions[stack].y + STACK_WIDTH / 2 + size * STACK_WIDTH - STACK_HEIGHT;
        }
    } else {
        if (stack < 13) {
            pos.y = stacksPositions[stack].y - STACK_WIDTH / 2 - 5 * STACK_WIDTH;         
        } else {
            pos.y = stacksPositions[stack].y + STACK_WIDTH / 2 - 5 * STACK_WIDTH - STACK_HEIGHT;
        }
    }
    myStones[stone].setPosition(pos);
    myStones[stone].setStackIndex(stack);
    stacks[stack].push(stone);
}

void pop(int stone)
{        
    currentStackIndex = myStones[stone].getStackIndex();
    if (stacks[currentStackIndex].top() == myStones[stone].getTag()) {
        currentStone = myStones[stone].getPosition();
        currentStoneIndex = stone;
        stacks[currentStackIndex].pop();
    }
}

void initStones()
{        
        bool first = true;
        if (playerId == 2) {
            first = false;
        }

        BGPoint stackPos;
        for (int i = 12; i >= 0; --i) {
                stackPos.y = FIRST_STACK_POS_Y - STACK_HEIGHT * ( i / 13 ) ;
                stackPos.x = FIRST_STACK_POS_X + STACK_WIDTH * ( i % 13 );
                if (i == 6) {
                    stackPos.x = FIRST_STACK_POS_X + STACK_WIDTH * ( i % 13 ) + STACK_WIDTH;
                }
                stacksPositions[i] = stackPos;
        }
        for (int i = 13; i < 26; ++i) {
                stackPos.y = FIRST_STACK_POS_Y - STACK_HEIGHT * ( i / 13 ) ;
                stackPos.x = FIRST_STACK_POS_X + STACK_WIDTH * ( i % 13 );
                if (i == 19) {
                    stackPos.x = FIRST_STACK_POS_X + STACK_WIDTH * ( i % 13 ) + STACK_WIDTH;
                }
                stacksPositions[i] = stackPos;
        }
        for (int i = 0; i < STONES_COUNT / 2; ++i) {
                myStones[i].createStone(i, 25, first);
                push(i,12);
        }
        for (int i = STONES_COUNT / 2; i < STONES_COUNT; ++i) {
                myStones[i].createStone(i, 25, !first);
                push(i,13);
        }
}

void init ()
{
//        string data;
//        ClientSocket clientSocket( "localhost", port );
//        try
//	{
//                clientSocket << "pleaseAddMe";
//                clientSocket >> data;
//                playerId = atoi( data.c_str());
//	}
//                catch ( SocketException& ) {
//        }
//        
//        ClientSocket clientSocket1( "localhost", port );
//        try
//	{
//                clientSocket1 << "Player";
//                clientSocket1 >> data;
//                cout<<"Data from player"<<data<<endl;
//        }
//                catch ( SocketException& ) {
//        }


        float diffuse_light[] = {1.0, 1.0, 1.0, 0.0};
        float ambient_light[] = {0.0, 0.0, 0.0, 0.0};
        glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
        glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse_light);
        glLightfv(GL_LIGHT1, GL_AMBIENT, ambient_light);
        textures.initTextures();
        stacks = new stack<int>[26];
        myStones = new BGStone[STONES_COUNT];
        stacksPositions = new BGPoint[26];
        initStones();

        glViewport (0, 0, windowWidth, windowHeight);
        glMatrixMode (GL_PROJECTION);
        glLoadIdentity();
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
}

float getDistance(BGPoint from, BGPoint to)
{
    return sqrt(pow((from.x - to.x) ,2) + pow((from.y - to.y), 2));
}

void touchDown(BGPoint touch)
{
    if (activePlayer == 0) {
//        string data;
//        ClientSocket clientSocket( "localhost", port );
//        try
//        {
//            clientSocket << "GetActivePlayer";
//            clientSocket >> data;
//            activePlayer = atoi(data.c_str());
//        }
//        catch ( SocketException& ) {
//        }
    }
    if (activePlayer == playerId) {
        float distance;
        for (int i = 0; i < STONES_COUNT / 2; ++i) {
                distance = getDistance(touch ,myStones[i].getPosition());
                if (distance < myStones[i].getRadius()){
                    if (myStones[i].getEnabled()) {
                        pop(i);
                    }
                }
        }
    }
}

bool pushIsCorrect(int curStone, int curStack)
{
    //TODO
    return true;
}

void touchUp(BGPoint touch)
{
    if(activePlayer != playerId) {
        return;
    }
    if (cube1 == 0 && cube2 == 0) {
//        string data;
//        ClientSocket clientSocket( "localhost", port );
//        try
//        {
//                 ostringstream ss;
//                 ss << playerId;
//                 string id = ss.str();
//                 clientSocket <<"End"<<id;
//          }
//          catch ( SocketException& ) {
//         }
        return;
    }

    for (int i = 0; i < 24; i++) {
        if ( touch.x > stacksPositions[i].x && touch.x < stacksPositions[i].x + STACK_WIDTH 
            && touch.y < stacksPositions[i].y && touch.y > stacksPositions[i].y - STACK_HEIGHT
                && pushIsCorrect(currentStoneIndex, i)) {
            if (currentStoneIndex != -1) {
                push(currentStoneIndex, i);
                string data;
//                ClientSocket clientSocket( "localhost", port );
//                try
//                {
//                    string id = static_cast<ostringstream*> (&(ostringstream() << playerId))->str();
//                    string curStone = static_cast<ostringstream*> (&(ostringstream() << currentStoneIndex))->str();
//                    string curStack = static_cast<ostringstream*> (&(ostringstream() << i))->str(); 
//                    clientSocket << "Player" << id <<"Send";
//                    clientSocket << curStone << ":" <<curStack;
//                }
//                  catch ( SocketException& ) {}
            }
            break;
        } else {
                myStones[currentStoneIndex].setPosition(currentStone);
                stacks[i].push(currentStoneIndex);
        }
    }
    currentStoneIndex = -1;
}

static void
mouse(int button, int state, int x, int y)
{
    BGPoint touch;
    touch.x = (float)x;
    touch.y = windowHeight - (float)y;
	if (button == GLUT_LEFT_BUTTON) {
		if (state == GLUT_DOWN) {
                    touchDown(touch);
		}
		if (state == GLUT_UP) {
                    touchUp(touch);
		}
	}
        glutPostRedisplay();
}

static void
motion(int x, int y)
{
    BGPoint touch;
    touch.x = x;
    touch.y = windowHeight - y;
    if (currentStoneIndex != -1) {
        myStones[currentStoneIndex].setPosition(touch);
        myStones[currentStoneIndex].draw(1);
        glutPostRedisplay();
    }
}

void idle()
{
//    string data;
//    ClientSocket clientSocket( "localhost", port );
//    try
//    {
//        string id = static_cast<ostringstream*> (&(ostringstream() << playerId))->str();
//        clientSocket << "Player" << id <<"Wait";
//        clientSocket >> data;
//    } catch ( SocketException& ) {}

}

void reshape (int w, int h) 
{
        windowWidth = w;
        windowHeight = h;
        glViewport (0, 0, (GLsizei)w, (GLsizei)h);
        glMatrixMode (GL_PROJECTION);
        glLoadIdentity();
        gluPerspective (42.0, (GLfloat)w / (GLfloat)h, 0.1, 1000000.0);
        glMatrixMode (GL_MODELVIEW);
        glLoadIdentity();
        gluLookAt(camera_x, camera_y, camera_z, center_x, center_y, center_z, 0.0, 1.0, 0.0);
}

int main(int argc, char **argv)
{
        glutInit(&argc, argv);
        glutInitDisplayMode (GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB);
        glutInitWindowSize (windowWidth, windowHeight);
        glutInitWindowPosition (100, 100);
        glutCreateWindow("Backgammon");
        init();
	glutMouseFunc(mouse);
	glutMotionFunc(motion);
	glutDisplayFunc(display);
//	glutIdleFunc(idle); 
        glutReshapeFunc(reshape); 
        glutMainLoop();
}
